package infra

import (
	"errors"
	"time"

	"gitee.com/hgslb/base"
	"gitee.com/hgslb/facade/fdns"
	"google.golang.org/protobuf/proto"
)

var kvDb *KvDb = &KvDb{}

func Start() {
	kvDb.StartDb()
}

func Stop() {
	kvDb.Close()
}

func SetDomain(key string, ttl int32, ipList []*fdns.GlbsIP) {
	old, err := GetDomain(key)
	kvDb.mu.Lock()
	glbsDns := &fdns.GlbsDomain{}
	glbsDns.Domain = key
	glbsDns.Ctime = time.Now().UnixMilli()
	glbsDns.Ttl = ttl
	glbsDns.IpList = make([]*fdns.GlbsIP, 20)
	existMap := make(map[string]bool)
	if err == nil {
		for _, ip := range old.IpList {
			glbsDns.IpList = append(glbsDns.IpList, ip)
			existMap[ip.Ip] = true
		}
	}
	for _, ip := range ipList {
		if _, ok := existMap[ip.Ip]; !ok {
			glbsDns.IpList = append(glbsDns.IpList, ip)
		}
	}
	buf, err := proto.Marshal(glbsDns)
	if err != nil {
		base.GslbLog.Infof("Marshal key:%s failed:%s", key, err)
	} else {
		kvPair := &KvPair{Key: key, Value: buf}
		err = kvDb.Update(DOMAIN_TABLE, kvPair)
		if err != nil {
			base.GslbLog.Infof("Update key:%s failed:%s", key, err)
		}
	}
	kvDb.mu.Unlock()
}

func GetDomain(key string) (*fdns.GlbsDomain, error) {
	kvDb.mu.Lock()
	kv, err := kvDb.GetByKey(DOMAIN_TABLE, key)
	kvDb.mu.Unlock()

	if err != nil {
		return nil, err
	}
	glbsDns := &fdns.GlbsDomain{}
	err = proto.Unmarshal(kv.Value, glbsDns)
	if err != nil {
		base.GslbLog.Infof("Unmarshal key:%s failed:%s", key, err)
		return nil, err
	}
	ttl := int32((time.Now().UnixMilli() - glbsDns.Ctime) / 1000)
	if ttl > glbsDns.Ttl {
		base.GslbLog.Debugf("key :%s is out ttl: %d > %d", key, ttl, glbsDns.Ttl)
		return nil, errors.New("ttl is out")
	}
	return glbsDns, nil
}

func AddDomainZoneIps(key string, ipList []*fdns.GlbsZoneIps) {
	kvDb.mu.Lock()
	kv, err := kvDb.GetByKey(DOMAIN_IP_TABLE, key)
	domainZone := &fdns.GlbsDomainZoneIps{}
	zoneIps := make(map[string]*fdns.GlbsZoneIps)
	if err == nil {
		err = proto.Unmarshal(kv.Value, domainZone)
		if err != nil {
			base.GslbLog.Infof("Unmarshal key:%s failed:%s", key, err)
			domainZone.ZoneIps = zoneIps
			domainZone.Domain = key
		}
	} else {
		domainZone.ZoneIps = zoneIps
		domainZone.Domain = key
	}
	for _, it := range ipList {
		domainZone.ZoneIps[it.Zone] = it
	}
	buf, err := proto.Marshal(domainZone)
	if err != nil {
		base.GslbLog.Infof("Marshal key:%s failed:%s", key, err)
	} else {
		kvPair := &KvPair{Key: key, Value: buf}
		err = kvDb.Update(DOMAIN_IP_TABLE, kvPair)
		if err != nil {
			base.GslbLog.Infof("Update key:%s failed:%s", key, err)
		}
	}
	kvDb.mu.Unlock()
}

func GetDomainZoneIps(key string) (*fdns.GlbsDomainZoneIps, error) {
	kvDb.mu.Lock()
	kv, err := kvDb.GetByKey(DOMAIN_IP_TABLE, key)
	kvDb.mu.Unlock()
	if err != nil {
		return nil, err
	}
	domainZone := &fdns.GlbsDomainZoneIps{}
	err = proto.Unmarshal(kv.Value, domainZone)
	if err != nil {
		base.GslbLog.Infof("Unmarshal key:%s failed:%s", key, err)
		return nil, err
	}
	return domainZone, nil
}

func AddDomainContry(key string, contry map[string]string) {
	kvDb.mu.Lock()
	contryZone := &fdns.GlbsCountryZone{}
	kv, err := kvDb.GetByKey(ZONE_COUNTRY_TABLE, key)
	if err == nil {
		err = proto.Unmarshal(kv.Value, contryZone)
		if err != nil {
			base.GslbLog.Infof("Unmarshal key:%s failed:%s", key, err)
			contryZone.ContryZone = make(map[string]string)
			contryZone.Domain = key
		}
	} else {
		contryZone.ContryZone = make(map[string]string)
		contryZone.Domain = key
	}
	for k, v := range contry {
		contryZone.ContryZone[k] = v
	}
	buf, err := proto.Marshal(contryZone)
	if err != nil {
		base.GslbLog.Infof("Marshal key:%s failed:%s", key, err)
	} else {
		kvPair := &KvPair{Key: key, Value: buf}
		err = kvDb.Update(ZONE_COUNTRY_TABLE, kvPair)
		if err != nil {
			base.GslbLog.Infof("Update key:%s failed:%s", key, err)
		}
	}
	kvDb.mu.Unlock()
}

func GetDomainZoneCounty(key string) (*fdns.GlbsCountryZone, error) {
	kvDb.mu.Lock()
	kv, err := kvDb.GetByKey(ZONE_COUNTRY_TABLE, key)
	kvDb.mu.Unlock()
	if err != nil {
		return nil, err
	}
	contryZone := &fdns.GlbsCountryZone{}
	err = proto.Unmarshal(kv.Value, contryZone)
	if err != nil {
		base.GslbLog.Infof("Unmarshal key:%s failed:%s", key, err)
		return nil, err
	}
	return contryZone, nil
}
